﻿/*
 * HyperStat.NET - The minimalist's static website compiler.
 * Copyright (C) 2012 Benjamin Brent
 * http://www.hyperstat.net/
 * http://hyperstat.codeplex.com/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;

namespace HyperStat
{
    partial class HyperServe
    {
        private string webRoot;
        private Dictionary<string, string> mimeTypes;

        /// <summary>
        /// Processes an inbound HTTP request.
        /// </summary>
        private void ProcessRequest(HttpListenerContext context)
        {
            string reqRoot = webRoot;

            // Get the request and response objects
            HttpListenerRequest request = context.Request;
            HttpListenerResponse response = context.Response;

            // Write request information to console
            Console.WriteLine("[" + DateTime.Now.ToString() + "]  " + request.RawUrl + " - " + request.RemoteEndPoint.Address.ToString() + " - " + request.UserAgent);

            // Attempt to on-demand compile the page 
            string[] reqParts = request.RawUrl.Split('/');
            string[] pageReq = reqParts[reqParts.Length - 1].Split('.');

            // If the page request is empty, assume index
            if (pageReq[0].Length == 0)
                pageReq[0] = "index";

            statGen.Generate(true, pageReq[0]);

            // The response objects
            Stream responseStream = null;
            byte[] buffer = null;

            // Attempt to retrieve the resource from the webroot
            try
            {
                string rawUrl = request.RawUrl;

                // Look for index.html if nothing else given
                if (rawUrl.EndsWith("/"))
                    rawUrl += "index.html";

                // Work out the MIME type
                string[] rawUrlDotSplit = rawUrl.Split('.');
                string extension = rawUrlDotSplit[rawUrlDotSplit.Length - 1];
                if (mimeTypes.ContainsKey(extension))
                    response.ContentType = mimeTypes[extension];
                else
                    response.ContentType = mimeTypes["bin"];

                // Send UTF-8 encoding if the MIME type is a text type
                if (response.ContentType.StartsWith("text/"))
                    response.ContentEncoding = Encoding.UTF8;

                // Read the file to an array and output
                string filePath = Path.Combine(reqRoot, rawUrl.TrimStart('/'));
                responseStream = File.OpenRead(filePath);
                response.StatusCode = 200;
            }
            catch (IOException)
            {
                response.StatusCode = 404;
                response.ContentType = "text/html";
                response.ContentEncoding = Encoding.UTF8;
                buffer = Encoding.UTF8.GetBytes(GenError("404 - File Not Found"));
            }
            catch (Exception)
            {
                response.StatusCode = 500;
                response.ContentType = "text/html";
                response.ContentEncoding = Encoding.UTF8;
                buffer = Encoding.UTF8.GetBytes(GenError("500 - Internal Server Error"));
            }

            // Write response stream
            Stream output = response.OutputStream;
            if (output != null && responseStream != null)
            {
                response.ContentLength64 = responseStream.Length;
                CopyStream(responseStream, output);
            }
            else if (output != null && buffer != null)
            {
                response.ContentLength64 = buffer.Length;
                output.Write(buffer, 0, buffer.Length);
                output.Close();
            }
        }

        /// <summary>
        /// Generates a simple HTML error page with the given message.
        /// </summary>
        /// <param name="message">Error message</param>
        /// <returns>HTML page</returns>
        private string GenError(string message)
        {
            return "<!DOCTYPE html><html><head></head><body><h1>" + message + "</h1></body></html>";
        }

        /// <summary>
        /// Writes from the input stream to the output stream, then closes both streams.
        /// </summary>
        /// <param name="input">Input stream.</param>
        /// <param name="output">Output stream.</param>
        /// <param name="bufferSize">Buffer size.</param>
        public static void CopyStream(Stream input, Stream output, int bufferSize = 8192)
        {
            if (input == null || output == null)
                throw new NullReferenceException("One of the streams was null!");

            byte[] buffer = new byte[8192];

            int bytesRead;

            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                output.Write(buffer, 0, bytesRead);

            output.Close();
            input.Close();
        }

        /// <summary>
        /// Creates/defines temporary webroot, clears webroot, sets project output to temporary webroot, compiles project.
        /// </summary>
        private void ConfigureHystProject()
        {
            // Add MIME types
            #region MIME Types

            mimeTypes = new Dictionary<string, string>
            {
                {"html", "text/html"},
                {"png", "image/png"},
                {"jpg", "image/jpeg"},
                {"jfif", "image/jpeg"},
                {"jpeg", "image/jpeg"},
                {"gif", "image/gif"},
                {"css", "text/css"},
                {"js", "text/javascript"},
                {"htm", "text/html"},
                {"shtml", "text/html"},
                {"bin", "application/octet-stream"},
                {"doc", "application/msword"},
                {"dot", "application/msword"},
                {"xls", "application/vnd.ms-excel"},
                {"ppt", "application/vnd.ms-powerpoint"},
                {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
                {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
                {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
                {"swf", "application/x-shockwave-flash"},
                {"xap", "application/x-silverlight-app"},
                {"sql", "application/x-sql"},
                {"xaml", "application/xaml+xml"},
                {"xml", "application/xml"},
                {"dtd", "application/xml-dtd"},
                {"zip", "application/zip"},
                {"exe", "application/x-msdownload"},
                {"rar", "application/x-rar-compressed"},
                {"7z", "application/x-7z-compressed"},
                {"cab", "application/vnd.ms-cab-compressed"},
                {"dll", "application/x-msdownload"},
                {"msi", "application/x-msdownload"},
                {"ipk", "application/vnd.shana.informed.package"},
                {"ipa", "application/octet-stream"},
                {"apk", "application/vnd.android.package-archive"},
                {"iso", "application/x-iso9660-image"},
                {"bz", "application/x-bzip"},
                {"bz2", "application/x-bzip2"},
                {"tar", "application/x-tar"},
                {"gz", "application/x-gzip"},
                {"pdf", "application/pdf"},
                {"ini", "text/plain"},
                {"inf", "text/plain"},
                {"txt", "text/plain"},
                {"dat", "application/octet-stream"},
                {"mov", "video/quicktime"},
                {"qt", "video/quicktime"},
                {"mp4", "video/mp4"},
                {"wmv", "video/x-ms-wmv"},
                {"mp3", "audio/mpeg"},
                {"mp2", "audio/mpeg"},
                {"flac", "audio/x-flac"},
                {"wma", "audio/x-ms-wma"},
                {"weba", "audio/webm"},
                {"flv", "video/x-flv"},
                {"mkv", "video/x-matroska"},
                {"wav", "audio/x-wav"},
                {"bmp", "image/bmp"},
                {"psd", "image/vnd.adobe.photoshop"},
                {"tiff", "image/tiff"},
                {"tif", "image/tiff"},
                {"svg", "image/svg+xml"},
                {"ico", "image/x-icon"},
                {"ics", "text/calendar"},
                {"conf", "text/plain"},
                {"text", "text/plain"},
                {"def", "text/plain"},
                {"list", "text/plain"},
                {"log", "text/plain"},
                {"in", "text/plain"},
                {"m4v", "video/x-m4v"},
                {"avi", "video/x-msvideo"},
                {"movie", "video/x-sgi-movie"},
                {"webm", "video/webm"},
                {"atom", "application/atom+xml"},
                {"cs", "text/plain"},
                {"vb", "text/plain"},
                {"sln", "text/xml"},
                {"cdproj", "text/xml"},
                {"vproj", "text/xml"},
                {"csv", "text/csv"},
                {"daa", "application/octet-stream"}
            };
            #endregion

            // Create the temporary webroot and delete any old ones
            webRoot = Path.Combine(Path.GetTempPath(), @"HyperServe.tmp");
            DirectoryInfo dirInfo = new DirectoryInfo(webRoot);
            if(dirInfo.Exists)
                dirInfo.Delete(true);
            Directory.CreateDirectory(webRoot);

            // Set project output to webroot
            statGen.OutputDir = webRoot;

            // Compile the complete project once
            statGen.Generate(false);
        }
    }
}
